package org.scs.carleton.ca.pgdt.core.ui.internal.highlighting;

import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.scs.carleton.ca.pgdt.core.ui.editors.PrologEditor;

/**
 * Manages and oversees all things dealing with highlighting
 * 
 * 
 * @author tessierj
 *
 */
public class PrologHighlightingManager {
	/** The source viewer */
	private ISourceViewer sourceViewer;
	
	/** The source viewer configuration */
	private PrologSourceViewerConfiguration configuration;
	
	/** The highlighting presenter */
	private PrologHighlightingPresenter highlightingPresenter;
	
	/** The highlighting reconciler */
	private PrologHighlightingReconciler highlightingReconciler;
	
	/** The presentation reconciler */
	private PrologPresentationReconciler presentationReconciler;
	
	/**
	 * Installs the highlighting onto a particular editor
	 * 
	 * @param editor the Prolog editor
	 * @param viewer the source viewer
	 * @param colorManager the color manager
	 */
	public void install(PrologEditor editor, ISourceViewer viewer, ColorManager colorManager) {
		this.sourceViewer = viewer;
		this.highlightingPresenter = new PrologHighlightingPresenter();
		
		if (editor != null) {
			configuration = editor.createPrologSourceViewerConfiguration();
			presentationReconciler = (PrologPresentationReconciler) configuration.getPresentationReconciler(sourceViewer);
			highlightingReconciler = new PrologHighlightingReconciler(colorManager);
		}
		else {
			configuration = null;
			presentationReconciler = null;
			highlightingReconciler = null;
		}
		
		highlightingPresenter.install(sourceViewer, presentationReconciler);
		if (highlightingReconciler != null)
			highlightingReconciler.install(editor, sourceViewer, highlightingPresenter);
	}
	
	public PrologHighlightingPresenter getHighlightingPresenter() {
		return highlightingPresenter;
	}

	public PrologHighlightingReconciler getHighlightingReconciler() {
		return highlightingReconciler;
	}

	public PrologPresentationReconciler getPresentationReconciler() {
		return presentationReconciler;
	}

	/**
	 * Removes the highlighting from the current environment
	 */
	public void uninstall() {
		sourceViewer = null;
		highlightingPresenter = null;
		configuration = null;
		presentationReconciler = null;
		highlightingReconciler = null;
	}
	
	/**
	 * Highlighting.  Shamelessly stolen from JDT
	 */
	static class HighlightingStyle {
		/** Text attribute */
		private TextAttribute textAttribute;
		/** Enabled state */
		private boolean isEnabled;

		/**
		 * Initialize with the given text attribute.
		 * @param textAttribute The text attribute
		 * @param isEnabled the enabled state
		 */
		public HighlightingStyle(TextAttribute textAttribute, boolean isEnabled) {
			setTextAttribute(textAttribute);
			setEnabled(isEnabled);
		}

		/**
		 * @return Returns the text attribute.
		 */
		public TextAttribute getTextAttribute() {
			return textAttribute;
		}

		/**
		 * @param textAttribute The background to set.
		 */
		public void setTextAttribute(TextAttribute textAttribute) {
			this.textAttribute = textAttribute;
		}

		/**
		 * @return the enabled state
		 */
		public boolean isEnabled() {
			return isEnabled;
		}

		/**
		 * @param isEnabled the new enabled state
		 */
		public void setEnabled(boolean isEnabled) {
			this.isEnabled = isEnabled;
		}
	}
	
	/**
	 * Highlighted Positions.  Shamelessly stolen from JDT
	 */
	static class HighlightedPosition extends Position {

		/** Highlighting of the position */
		private HighlightingStyle style;

		/** Lock object */
		private Object lock;

		/**
		 * Initialize the styled positions with the given offset, length and foreground color.
		 *
		 * @param offset The position offset
		 * @param length The position length
		 * @param highlighting The position's highlighting
		 * @param lock The lock object
		 */
		public HighlightedPosition(int offset, int length, HighlightingStyle style, Object lock) {
			super(offset, length);
			this.style = style;
			this.lock = lock;
		}

		/**
		 * @return Returns a corresponding style range.
		 */
		public StyleRange createStyleRange() {
			int len= 0;
			if (style.isEnabled())
				len= getLength();

			TextAttribute textAttribute = style.getTextAttribute();
			int style = textAttribute.getStyle();
			int fontStyle = style & (SWT.ITALIC | SWT.BOLD | SWT.NORMAL);
			StyleRange styleRange = new StyleRange(getOffset(), len, textAttribute.getForeground(), textAttribute.getBackground(), fontStyle);
			styleRange.strikeout = (style & TextAttribute.STRIKETHROUGH) != 0;
			styleRange.underline = (style & TextAttribute.UNDERLINE) != 0;

			return styleRange;
		}

		/**
		 * Uses reference equality for the highlighting.
		 *
		 * @param off The offset
		 * @param len The length
		 * @param highlighting The highlighting
		 * @return <code>true</code> iff the given offset, length and highlighting are equal to the internal ones.
		 */
		public boolean isEqual(int off, int len, HighlightingStyle highlighting) {
			synchronized (lock) {
				return !isDeleted() && getOffset() == off && getLength() == len && style == highlighting;
			}
		}

		/**
		 * Is this position contained in the given range (inclusive)? Synchronizes on position updater.
		 *
		 * @param off The range offset
		 * @param len The range length
		 * @return <code>true</code> iff this position is not delete and contained in the given range.
		 */
		public boolean isContained(int off, int len) {
			synchronized (lock) {
				return !isDeleted() && off <= getOffset() && off + len >= getOffset() + getLength();
			}
		}

		public void update(int off, int len) {
			synchronized (lock) {
				super.setOffset(off);
				super.setLength(len);
			}
		}

		/*
		 * @see org.eclipse.jface.text.Position#setLength(int)
		 */
		public void setLength(int length) {
			synchronized (lock) {
				super.setLength(length);
			}
		}

		/*
		 * @see org.eclipse.jface.text.Position#setOffset(int)
		 */
		public void setOffset(int offset) {
			synchronized (lock) {
				super.setOffset(offset);
			}
		}

		/*
		 * @see org.eclipse.jface.text.Position#delete()
		 */
		public void delete() {
			synchronized (lock) {
				super.delete();
			}
		}

		/*
		 * @see org.eclipse.jface.text.Position#undelete()
		 */
		public void undelete() {
			synchronized (lock) {
				super.undelete();
			}
		}

		/**
		 * @return Returns the highlighting.
		 */
		public HighlightingStyle getHighlighting() {
			return style;
		}
	}
}
